﻿namespace NTLib.Core.Tasks
{
    using System;
    using System.Threading;
    using System.Threading.Tasks;

    /// <summary>
    /// Extensions basic method to improve the task univers
    /// </summary>
    public static class TaskExtensions
    {
        #region Methods

        /// <summary>
        /// Attaches the cancel token.
        /// </summary>
        /// <returns>Return the task completion source</returns>
        public static TaskCompletionSource<T> AttachCancelToken<T>(this TaskCompletionSource<T> t, CancellationToken token)
        {
            token.Register((o) => t.TrySetCanceled(), t, true);
            return t;
        }

        /// <summary>
        /// Waits the task to be stop
        /// </summary>
        /// <returns>Task result</returns>
        public static T WaitResult<T>(this Task<T> t, CancellationToken token = default(CancellationToken))
        {
            Task.Run(() => t).Wait(token);
            return t.Result;
        }

        /// <summary>
        /// Waits the task to be stop
        /// </summary>
        /// <returns>Task result</returns>
        public static Task<T> AttachCancelToken<T>(this Task<T> t, CancellationToken token = default(CancellationToken))
        {
            return Task.Run(() =>
            {
                t.Wait(token);
                return t.Result;
            });
        }

        /// <summary>
        ///    Creates a System.Threading.CancellationTokenSource that will be in the canceled
        ///     state when any of the source tokens in the specified array are in the canceled
        ///     state.
        /// </summary>
        /// <seealso cref="CancellationTokenSource.CreateLinkedTokenSource(CancellationToken[])"/>
        /// <param name="sourceA">The source a.</param>
        /// <param name="sourceB">The source b.</param>
        /// <returns></returns>
        public static CancellationTokenSource CreateLink(this CancellationTokenSource sourceA, CancellationTokenSource sourceB)
        {
            return sourceA.Token.CreateLink(sourceB.Token);
        }

        /// <summary>
        ///    Creates a System.Threading.CancellationTokenSource that will be in the canceled
        ///     state when any of the source tokens in the specified array are in the canceled
        ///     state.
        /// </summary>
        /// <seealso cref="CancellationTokenSource.CreateLinkedTokenSource(CancellationToken[])"/>
        /// <param name="tokenA">The source a.</param>
        /// <param name="tokenB">The source b.</param>
        /// <returns></returns>
        public static CancellationTokenSource CreateLink(this CancellationToken tokenA, CancellationToken tokenB)
        {
            return CancellationTokenSource.CreateLinkedTokenSource(tokenA, tokenB);
        }

        /// <summary>
        /// To the task.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static async Task ToTask(this IAsyncResult result, CancellationToken token = default(CancellationToken))
        {
            await Task.Run(() =>
            {
                while (result.IsCompleted == false || token.IsCancellationRequested)
                    result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(10));
            }, token);
        }

        /// <summary>
        /// To the task.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="result">The result.</param>
        /// <param name="endMethod">The end method.</param>
        /// <param name="token">The token.</param>
        public static async Task<TResult> ToTask<TResult>(this IAsyncResult result, Func<IAsyncResult, CancellationToken, TResult> endMethod, CancellationToken token = default(CancellationToken))
        {
            if (endMethod == null)
                throw new ArgumentNullException(nameof(endMethod));

            await Task.Run(() =>
            {
                while (result.IsCompleted == false || token.IsCancellationRequested)
                    result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(10));
            }, token);

            return endMethod(result, token);
        }

        #endregion
    }
}
